﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Ue.Core.DynamicReflection;

namespace Ue.Extensions
{
    public static class DynamicReflectionExtensions
    {
        #region Method

        public static object DynamicMethodInvoke(this object instance, string methodName, params object[] parameters)
        {
            return DynamicExecute(
                instance,
                "methodName",
                () => instance.DynamicMethodInvoke(type => type.GetMethod(methodName),parameters));
        }
        public static object DynamicMethodInvoke(this object instance, Func<Type, MethodInfo> acquireMethod, params object[] parameters)
        {
            return DynamicExecute<MethodInfo>(
                instance,
                type => acquireMethod(type),
                method => method.DynamicMethodInvoke(instance, parameters));
        }
        public static object DynamicMethodInvoke(this MethodInfo methodInfo, object instance, params object[] parameters)
        {
            return DynamicReflectionCaches.MethodInvokerCache.Get(methodInfo).Invoke(instance, parameters);
        }

        #endregion

        #region Property

        public static void DynamicPropertySetValue(this object instance, object value, string propertyName)
        {
            DynamicExecute(
                instance,
                "propertyName",
                () =>
                {
                    instance.DynamicPropertySetValue(value, type => type.GetProperty(propertyName));
                    return null;
                });
        }
        public static void DynamicPropertySetValue(this object instance, object value, Func<Type, PropertyInfo> acquireTarget)
        {
            DynamicExecute<PropertyInfo>(
               instance,
               type => acquireTarget(type),
               property =>
               {
                   property.DynamicPropertySetValue(instance, value);
                   return null;
               });
        }
        public static void DynamicPropertySetValue(this PropertyInfo propertyInfo, object instance, object value)
        {
            DynamicReflectionCaches.PropertyAccessorCache.Get(propertyInfo).SetValue(instance, value);
        }

        public static object DynamicPropertyGetValue(this object instance, string propertyName)
        {
            return DynamicExecute(
                instance,
                "propertyName",
                () => instance.DynamicPropertyGetValue(type => type.GetProperty(propertyName)));
        }
        public static object DynamicPropertyGetValue(this object instance,Func<Type,PropertyInfo> acquireTarget)
        {
            return DynamicExecute<PropertyInfo>(
                instance,
                type => acquireTarget(type),
                property => property.DynamicPropertyGetValue(instance));
        }
        public static object DynamicPropertyGetValue(this PropertyInfo propertyInfo, object instance)
        {
            return DynamicReflectionCaches.PropertyAccessorCache.Get(propertyInfo).GetValue(instance);
        }

        #endregion

        #region Field

        public static object DynamicFieldGetValue(this object instance, string fieldName)
        {
            return DynamicExecute(
                instance,
                "fieldName",
                () => instance.DynamicFieldGetValue(type => type.GetField(fieldName)));
        }
        public static object DynamicFieldGetValue(this object instance, Func<Type, FieldInfo> acquireField)
        {
            return DynamicExecute<FieldInfo>(
                instance,
                type => acquireField(type),
                field => field.DynamicFieldGetValue(instance));
        }
        public static object DynamicFieldGetValue(this FieldInfo fieldInfo, object instance)
        {
            return DynamicReflectionCaches.FieldAccessorCache.Get(fieldInfo).GetValue(instance);
        }

        #endregion

        #region Constructor

        public static object DynamicConstructor(this Type type, Type[] argTypes, params object[] parameters)
        {
            return DynamicExecute<ConstructorInfo>(
                type,
                t => type.GetConstructor(argTypes),
                constructor => constructor.DynamicConstructor(parameters));
        }
        public static object DynamicConstructor(this ConstructorInfo constructorInfo, params object[] parameters)
        {
            return DynamicReflectionCaches.ConstructorInvokerCache.Get(constructorInfo).Invoke(parameters);
        }

        #endregion

        #region Helper

        private static object DynamicExecute(object instance, string targetName,Func<object> articleReturn)
        {
            targetName.ArgumentNullExceptionByNullOrEmpty(targetName);

            return articleReturn();
        }

        private static object DynamicExecute<T>(object instance, Func<Type,T> acquireTarget, Func<T, object> acquireReturn)
        {
            instance.ArgumentNullExceptionByNullOrEmpty("instance");

            T t = acquireTarget(instance.GetType());
            if (t != null)
            {
                return acquireReturn(t);
            }

            return null;
        }

        #endregion
    }
}
